package com.aop.demo.aop;

import com.aop.demo.annotation.AopLog;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Objects;

@Aspect
@Component
public class WebCommonLogAspect {
    public static final Logger LOGGER = LoggerFactory.getLogger(WebCommonLogAspect.class);

    private final ThreadLocal<Instant> time = new ThreadLocal<>();

    @Pointcut("@annotation(com.aop.demo.annotation.AopLog)")
    public void doPointcut() {
    }

    @Around(value = "doPointcut()")
    public Object  around(ProceedingJoinPoint joinPoint) {
        Object proceed = null;
        try {
            time.set(Instant.now());

            //前置通知
            beforePrint(joinPoint);

            proceed = joinPoint.proceed();

            //返回通知
            LOGGER.info("result:{}", proceed);

        } catch (Throwable e) {
            //异常通知
            LOGGER.info("抛出异常：{}", e);
        }

        //后置通知(无论方法是否发生异常都会执行)
        LOGGER.info("\n ==> 一共耗时：{} MS", Duration.between(time.get(), Instant.now()).toMillis());
        time.remove();

        LOGGER.info("\n ==> END with {}", parseAopLog(joinPoint).value());

        return proceed;
    }

    private AopLog parseAopLog(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        //方式一
       /* MethodSignature methodSignature = (MethodSignature)signature;
        Method method = methodSignature.getMethod();
        AopLog aopLog = method.getAnnotation(AopLog.class);
        return aopLog;*/

        //方式二
        Class<MethodSignature> methodSignatureClass = MethodSignature.class;
        if (methodSignatureClass.isInstance(signature)) {
            return methodSignatureClass.cast(signature).getMethod().getAnnotation(AopLog.class);
        } else {
            return null;
        }
    }

    public void beforePrint(ProceedingJoinPoint joinPoint) {
        try {
            AopLog aopLog = parseAopLog(joinPoint);
            LOGGER.info("\n ==> START with {}", aopLog.value());

            //获取方法参数
            Object[] args = joinPoint.getArgs();

            if (Objects.nonNull(args)) {
                LOGGER.info("\n ==> 参数为 {}", Arrays.toString(args));
            }

            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            LOGGER.info("\n ==> 请求的URL: {}, Method: {}", request.getRequestURL(), request.getMethod());

            LOGGER.info("\n ====> REQUEST_METHOD : {}", joinPoint.toLongString().replaceAll("execution", ""));
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}
